﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
//using System.Windows.Shapes;
using Microsoft.Phone.Controls;

using ESRI.ArcGIS.Client;
using ESRI.ArcGIS.Client.Geometry;
using ESRI.ArcGIS.Client.Tasks;
using ESRI.ArcGIS.Client.Symbols;

using System.Device.Location;

namespace BEO
{
    public partial class Optimize : PhoneApplicationPage
    {
        RouteTask _routeTask;
        RouteParameters _routeParams = new RouteParameters();

        List<Graphic> _stops = new List<Graphic>();

        GraphicsLayer _stopsLayer = null;
        GraphicsLayer _routeLayer = null;

        List<Errand> _errandList = new List<Errand>();

        ErrandCollection _errands;

        GeoCoordinateWatcher _geoWatcher;

        bool _bLocationObtained;
        bool _bLocationServiceOn;
        
        ESRI.ArcGIS.Client.Geometry.Envelope _initExtent;

        Guid _homeGuid;

        private static ESRI.ArcGIS.Client.Projection.WebMercator mercator =
            new ESRI.ArcGIS.Client.Projection.WebMercator();

        public Optimize()
        {
            InitializeComponent();

            _routeTask = new RouteTask("http://tasks.arcgisonline.com/ArcGIS/rest/services/NetworkAnalysis/ESRI_Route_NA/NAServer/Route");
            _routeTask.SolveCompleted += routeTask_SolveCompleted;
            _routeTask.Failed += routeTask_Failed;

            _routeParams.Stops = _stops;
            _routeParams.UseTimeWindows = false;
            _routeParams.ReturnStops = true;

            _routeParams.FindBestSequence = true;
            _routeParams.PreserveFirstStop = false;
            _routeParams.PreserveLastStop = false;

            _stopsLayer = OptimizeMap.Layers["OptimizeStops"] as GraphicsLayer;
            _routeLayer = OptimizeMap.Layers["OptimizeRoute"] as GraphicsLayer;

            _initExtent = OptimizeMap.Extent;

            //
            // Use current GPS location as start and end locations
            //
            _bLocationObtained = false;
            _bLocationServiceOn = false;

            _geoWatcher = new GeoCoordinateWatcher();
            _geoWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(_geoWatcher_PositionChanged);

            _geoWatcher.TryStart(false, TimeSpan.FromMilliseconds(100));
            if (_geoWatcher.Permission != GeoPositionPermission.Denied)
            {
                // location service is on
                _bLocationServiceOn = true;
                _geoWatcher.Start();
            }

            _homeGuid = System.Guid.Empty;
        }

        void _geoWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {
            _geoWatcher.Stop();

            if (_bLocationServiceOn && !_bLocationObtained)
            {
                if (Errands.Count == 0)
                {
                    string errorMessage = "Errand list is empty.";
                    MessageBox.Show(errorMessage);

                    return;
                }

                //
                // Add current GPS location as start and end points
                //
                GeoCoordinate geoCoord = e.Position.Location;

                if (!geoCoord.IsUnknown)
                {
                    Errand errand = new Errand();

                    errand.Id = Guid.NewGuid();
                    errand.Address = "";
                    errand.Title = "Home";
                    errand.Description = "GPS @ " + DateTime.Now.ToString();
                    errand.Latitude = geoCoord.Latitude;
                    errand.Longitude = geoCoord.Longitude;
                    errand.Time = 0;

                    // same errand is added twice
                    Errands.Insert(0, errand);
                    Errands.Add(errand);

                    _homeGuid = errand.Id;
                }

                _bLocationObtained = true;

                SendOptimizeRequest();
            }
        }

        public ErrandCollection Errands
        {
            get
            {
                if (_errands == null)
                {
                    ErrandCollection errands = (App.Current as App).Errands;

                    _errands = new ErrandCollection();

                    foreach (Errand errand in errands)
                    {
                        _errands.Add(errand);

                        /*
                        Errand errand2 = new Errand();

                        errand2.Address = errand1.Address;
                        errand2.Title = errand1.Title;
                        errand2.Description = errand1.Description;
                        errand2.Latitude = errand1.Latitude;
                        errand2.Longitude = errand1.Longitude;
                        errand2.Time = errand1.Time;

                        errands2.Add(errand2);
                        */
                    }
                }

                return _errands;
            }
        }

        private void routeTask_Failed(object sender, TaskFailedEventArgs e)
        {
            string errorMessage = "Routing Error: ";
            errorMessage += e.Error.Message;
            foreach (string detail in (e.Error as ServiceException).Details)
                errorMessage += " " + detail;

            MessageBox.Show(errorMessage);

            if (_homeGuid != System.Guid.Empty)
            {
                Errand errand2 = (from errand in Errands where errand.Id.Equals(_homeGuid) select errand).First();
                Errands.Remove(errand2);
                Errands.Remove(errand2);

                /*
                foreach (Errand errand in Errands)
                {
                    if (errand.Id == _homeGuid)
                    {
                        Errands.Remove(errand);
                        Errands.Remove(errand);
                        break;
                    }
                }
                */
            }
        }

        private void routeTask_SolveCompleted(object sender, RouteEventArgs e)
        {
            RouteResult routeResult = e.RouteResults[0];

            //MessageBox.Show("Route optimization completed.");
            mProgressLoading.Visibility = Visibility.Collapsed;
            mPivotControl.Visibility = Visibility.Visible;

            Errands.Clear();

            foreach (ESRI.ArcGIS.Client.Geometry.PointCollection path in (routeResult.Route.Geometry as Polyline).Paths)
            {
                foreach (MapPoint mapPoint in path)
                {
                    var matches =
                        from errand in _errandList
                        where Math.Abs(errand.Longitude - mapPoint.X) < 0.009 && Math.Abs(errand.Latitude - mapPoint.Y) < 0.009
                        select errand;

                    foreach (var match in matches)
                    {
                        Errands.Add(match);
                        _errandList.Remove((from errand in _errandList where errand.Id.Equals((match as Errand).Id) select errand).First());
                        break;
                    }
                }
            }

            //
            // Add text symbols
            //
            TextSymbol templateTextSymbol = LayoutRoot.Resources["StopTextSymbol"] as ESRI.ArcGIS.Client.Symbols.TextSymbol;

            for (int i = 0; i < Errands.Count; i++)
            {
                TextSymbol textSymbol = new TextSymbol()
                {
                    FontFamily = templateTextSymbol.FontFamily,
                    Foreground = templateTextSymbol.Foreground,
                    FontSize = templateTextSymbol.FontSize,
                    Text = (i + 1).ToString() + ". " + Errands[i].Title
                };

                Graphic stop = new Graphic()
                {
                    Geometry = mercator.FromGeographic(new MapPoint(Errands[i].Longitude, Errands[i].Latitude)),
                    Symbol = textSymbol
                };

                _stopsLayer.Graphics.Add(stop);
            }

            //
            // Add route symbols
            //
            routeResult.Route.Symbol = LayoutRoot.Resources["RouteSymbol"] as ESRI.ArcGIS.Client.Symbols.Symbol;
            _routeLayer.Graphics.Clear();
            _routeLayer.Graphics.Add(routeResult.Route);
        }

        private void SendOptimizeRequest()
        {
            _errandList.Clear();
            _stopsLayer.Graphics.Clear();

            foreach (Errand errand in Errands)
            {
                //
                // InteractiveWindowsPhoneSDKSource\ArcGISWindowsPhoneSDK\Samples\Device\ShowGPSLocation.xaml.cs
                // InteractiveWindowsPhoneSDKSource\ArcGISWindowsPhoneSDK\Samples\Network\RoutingBarriers.xaml.cs
                //
                Graphic stop = new Graphic()
                {
                    Geometry = mercator.FromGeographic(new MapPoint(errand.Longitude, errand.Latitude)),
                    Symbol = LayoutRoot.Resources["StopSymbol"] as ESRI.ArcGIS.Client.Symbols.Symbol
                };

                _stopsLayer.Graphics.Add(stop);
                _stops.Add(stop);

                if (_homeGuid != System.Guid.Empty && errand.Id == _homeGuid) // not to add start/end points
                {
                    continue;
                }

                _errandList.Add(errand);
            }

            if (_routeTask.IsBusy)
            {
                _routeTask.CancelAsync();
            }

            _routeTask.SolveAsync(_routeParams);
        }

        private void OptimizePage_Loaded(object sender, RoutedEventArgs e)
        {
            if (Errands.Count == 0)
            {
                string errorMessage = "Errand list is empty.";
                MessageBox.Show(errorMessage);

                return;
            }

            if (!_bLocationServiceOn)
            {
                SendOptimizeRequest();
            }
        }

        //
        // Add following to disable swiping gesture on pivot page
        // Map gets the swiping gesture
        //
        private void Grid_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
        {
            e.Handled = true;
        }

        private void Grid_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
        {
            e.Handled = true;
        }

        private void Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            e.Handled = true;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            OptimizeMap.Extent = _initExtent;
        }
    }
}